package com.videostar.funforfollowers.utils

import android.app.Activity
import android.content.Context
import android.text.TextUtils
import android.util.Base64
import com.android.billingclient.api.*
import java.io.IOException
import java.security.*
import java.security.spec.InvalidKeySpecException
import java.security.spec.X509EncodedKeySpec
import java.util.concurrent.ConcurrentHashMap

/**
 * @author Afra55
 * @date 2019-07-31
 * A smile is the best business card.
 * https://developer.android.com/google/play/billing/billing_overview?hl=zh-cn
 */
object GooglePay : PurchasesUpdatedListener {


    private const val TAG = "GOO"

    private lateinit var billingClient: BillingClient

    private var mIsServiceConnected = false
    private var mBillingClientResponseCode: Int? = null
    private var gooListener: GooUpdateListener? = null

    private lateinit var BASE_64_ENCODED_PUBLIC_KEY: String

    private val mPurchases = mutableListOf<GooResultBean>()
    private val gooSkuDetailsMap: ConcurrentHashMap<String, SkuDetails> = ConcurrentHashMap()
    private val checkSubsSkuMap: ConcurrentHashMap<String, Boolean> = ConcurrentHashMap()

    private val mTokensToBeConsumed = mutableSetOf<String>()

    private var isAutoConsume: Boolean = true

    fun init(
        context: Context,
        base64EncodedPublicKey: String,
        subsSkuList: MutableList<String>? = null,
        inappSkuList: MutableList<String>? = null,
        listener: GooUpdateListener? = null
    ) {

        BASE_64_ENCODED_PUBLIC_KEY = base64EncodedPublicKey

        gooListener = listener

        if (mIsServiceConnected) {
            gooListener?.onBillingClientSetupFinished()
            return
        }

        billingClient =
            BillingClient.newBuilder(context).enablePendingPurchases().setListener(this).build()


        startServiceConnection(Runnable {

            gooListener?.onBillingClientSetupFinished()

            // 连接 谷歌成功后，查询之前购买过的订单
            queryPurchases()

            if (!subsSkuList.isNullOrEmpty()) {
                querySkuDetailsAsync(true, subsSkuList)
            }
            if (!inappSkuList.isNullOrEmpty()) {
                querySkuDetailsAsync(false, inappSkuList)
            }


        })


    }

    // 订单状态更新
    override fun onPurchasesUpdated(
        billingResult: BillingResult,
        purchases: MutableList<Purchase>?
    ) {
        when {
            billingResult.responseCode == BillingClient.BillingResponseCode.OK -> {
                mPurchases.clear()
                if (purchases != null) {
                    for (p in purchases) {
                        handlePurchase(p)
                    }
                }
                gooListener?.onPurchasesUpdated(mPurchases)
            }
            billingResult.responseCode == BillingClient.BillingResponseCode.USER_CANCELED -> {
                gooListener?.errorByUserCanceled(billingResult.responseCode)
            }
            billingResult.responseCode == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED -> {
                gooListener?.error(billingResult.responseCode)
            }
            else -> {
                gooListener?.error(billingResult.responseCode ?: -1)
            }
        }
    }

    // 查询应用内商品详情
    fun querySkuDetailsAsync(
        isSubs: Boolean,
        skuList: MutableList<String>,
        detailsListener: GooSkuDetailsListener? = null
    ) {

        val queryRequest = Runnable {
            val params = SkuDetailsParams.newBuilder().setSkusList(skuList)
                .setType(if (isSubs) BillingClient.SkuType.SUBS else BillingClient.SkuType.INAPP)
                .build()
            billingClient.querySkuDetailsAsync(
                params
            ) { billingResult, skuDetailsList ->
                // SkuDetails: {"skuDetailsToken":"AEuhp4KtsOzNlUgfnWwM5ilDAnD4yZRe6_WRiFK0JE1bwaHVtVQQc5ClAhhdq3Hj5UxE","productId":"com.wallet.supersafeso.levelone","type":"inapp","price":"US$0.99","price_amount_micros":990000,"price_currency_code":"USD","title":"Beginner Pack (Fast Wallet)","description":"Beginner Pack\n"}
                if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {


                    val list = mutableListOf<GooSkuDetails>()
                    skuDetailsList?.forEach {
                        gooSkuDetailsMap[it.sku] = it
                        list.add(GooSkuDetails.cloneBySkuDetails(it))
                    }
                    detailsListener?.onSuccess(list)
                } else {
                    detailsListener?.error(billingResult.responseCode)
                }
            }
        }

        executeServiceRequest(queryRequest)
    }

    fun getSkuDetailsCacheBySku(sku: String): GooSkuDetails? {
        var skuDetial: GooSkuDetails? = null
        if (gooSkuDetailsMap.containsKey(sku)) {
            skuDetial = gooSkuDetailsMap[sku]?.let { GooSkuDetails.cloneBySkuDetails(it) }
        }
        return skuDetial
    }

    // 查询购买的订单
    fun queryPurchases() {
        val queryToExecute = Runnable {
            val startTime = System.currentTimeMillis()
            val purchasesResult = billingClient.queryPurchases(BillingClient.SkuType.INAPP)

            // 如果支持订阅
            if (areSubscriptionsSupported()) {
                val subscriptionResult = billingClient.queryPurchases(BillingClient.SkuType.SUBS)
                try {
                } catch (e: Exception) {
                }

                try {
                    if (subscriptionResult.responseCode == BillingClient.BillingResponseCode.OK) {
                        subscriptionResult.purchasesList?.let { purchasesResult.purchasesList?.addAll(it) }
                    } else {
                    }
                } catch (e: Exception) {
                }
            } else if (purchasesResult.responseCode == BillingClient.BillingResponseCode.OK) {
            } else {
            }

            onQueryPurchasesFinished(purchasesResult)
        }

        executeServiceRequest(queryToExecute)
    }

    // 启用应用内商品的购买
    fun toBuy(activity: Activity, skuId: String, isSubs: Boolean) {
        toBuy(activity, skuId, null, isSubs)

    }

    fun toBuy(activity: Activity, skuId: String, oldSku: String?, isSubs: Boolean) {
        checkSubsSkuMap.put(skuId, isSubs)
        val purchaseFlowRequest = Runnable {
            val skuDetails = gooSkuDetailsMap[skuId]
            if (skuDetails != null) {
                launchBillingFlow(skuDetails, oldSku, activity)
            } else {
                querySkuDetailsAsync(isSubs, mutableListOf(skuId), object : GooSkuDetailsListener {
                    override fun error(responseCode: Int) {
                        gooListener?.error(responseCode)
                    }

                    override fun onSuccess(skuDetails: MutableList<GooSkuDetails>) {

                        val skuDetailsFromGoo = gooSkuDetailsMap[skuId]
                        if (skuDetailsFromGoo != null) {
                            launchBillingFlow(skuDetailsFromGoo, oldSku, activity)
                        } else {
                            gooListener?.error(-2)
                        }
                    }

                })
            }
        }
        executeServiceRequest(purchaseFlowRequest)
    }

    // 确认购买交易
    fun consumeAsync(purchaseToken: String, developerPayload: String? = "") {
        if (mTokensToBeConsumed.contains(purchaseToken)) {
            return
        }
        mTokensToBeConsumed.add(purchaseToken)

        val onConsumeListener = ConsumeResponseListener { billingResult, pt ->
            gooListener?.onConsumeFinished(pt, billingResult.responseCode)
        }

        val consumeRequest = Runnable {
            val consumeParams = ConsumeParams.newBuilder().setPurchaseToken(purchaseToken)
//            consumeParams.setDeveloperPayload(developerPayload)
            billingClient.consumeAsync(consumeParams.build(), onConsumeListener)
        }

        executeServiceRequest(consumeRequest)

    }

    fun destory() {
    }

    private fun launchBillingFlow(
        skuDetails: SkuDetails?,
        oldSku: String?,
        activity: Activity
    ) {
        if (skuDetails != null) {

            val purchaseParams = BillingFlowParams
                    .newBuilder()
                    .setSkuDetails(skuDetails)
            if (!oldSku.isNullOrEmpty()) {
                // (String oldSku, String purchaseToken)
//                purchaseParams.setOldSku(oldSku, purchaseToken)
            }
            billingClient.launchBillingFlow(activity, purchaseParams.build())
        }
    }

    // 执行 server 任务，如果之前没有连接谷歌成功，重新连接并执行任务
    private fun executeServiceRequest(runnable: Runnable) {
        if (mIsServiceConnected) {
            runnable.run()
        } else {
            startServiceConnection(runnable)
        }
    }

    // 处理查询到的订单
    private fun onQueryPurchasesFinished(purchasesResult: Purchase.PurchasesResult) {
        try {
            if (purchasesResult.responseCode != BillingClient.BillingResponseCode.OK) {
                return
            }

            val billingResult =
                BillingResult.newBuilder().setResponseCode(BillingClient.BillingResponseCode.OK)
                    .build()
            val purchases = purchasesResult.purchasesList
            onPurchasesUpdated(billingResult, purchases)
        } catch (e: Exception) {
        }


    }

    // 是否支持订阅
    private fun areSubscriptionsSupported(): Boolean {
        val code = billingClient.isFeatureSupported(BillingClient.FeatureType.SUBSCRIPTIONS)
        return if (code.responseCode != BillingClient.BillingResponseCode.OK) {

            false
        } else {
            true
        }
    }

    // 检查完成的订单是否有效
    private fun handlePurchase(purchase: Purchase) {

        if (!verifyValidSignature(purchase.originalJson, purchase.signature)) {
            return
        }


        if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
            // 已经支付的订单
            if (checkSkuIsSub(purchase.sku)) {
                if (!purchase.isAcknowledged) {
                    // 确认订阅订单
                    val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
                        .setPurchaseToken(purchase.purchaseToken)
//                        .setDeveloperPayload(purchase.developerPayload)
                        .build()
                    billingClient.acknowledgePurchase(
                        acknowledgePurchaseParams
                    ) {

                    }
                }
            } else {
                if (isAutoConsume) {
                    // 消耗消耗型订单
                    consumeAsync(purchase.purchaseToken, purchase.developerPayload)
                }
            }

        } else if (purchase.purchaseState == Purchase.PurchaseState.PENDING) {
            // Here you can confirm to the user that they've started the pending
            // purchase, and to complete it, they should follow instructions that
            // are given to them. You can also choose to remind the user in the
            // future to complete the purchase if you detect that it is still
            // pending.
        }

        mPurchases.add(
            getPiePayResultByPurchase(purchase)
        )


    }

    private fun checkSkuIsSub(sku: String): Boolean {
        return try {
            checkSubsSkuMap[sku] ?: false
        } catch (e: Exception) {
            false
        }
    }


    private fun getPiePayResultByPurchase(purchase: Purchase): GooResultBean {
        return GooResultBean.cloneByPurchase(purchase)
    }

    // 连接到 Google Play
    private fun startServiceConnection(executeOnSuccess: Runnable) {
        billingClient.startConnection(object : BillingClientStateListener {
            override fun onBillingSetupFinished(billingResult: BillingResult) {

                mBillingClientResponseCode = billingResult.responseCode.let { responseCode ->
                    if (responseCode == BillingClient.BillingResponseCode.OK) {
                        mIsServiceConnected = true
                        executeOnSuccess.run()
                    } else {
                        gooListener?.onBillingClientSetupError()
                    }
                    responseCode
                }

            }

            override fun onBillingServiceDisconnected() {
                mIsServiceConnected = false
                gooListener?.onBillingClientSetupError()
            }

        })
    }

    //  验证订单安全性
    private fun verifyValidSignature(signedData: String, signature: String): Boolean {

        if (BASE_64_ENCODED_PUBLIC_KEY.isEmpty()) {
            throw RuntimeException("Base 64 code is wrong")
        }

        return try {
            GooSecurity.verifyPurchase(BASE_64_ENCODED_PUBLIC_KEY, signedData, signature)
        } catch (e: Exception) {
            false
        }

    }
}

interface GooUpdateListener {
    fun onBillingClientSetupFinished()
    fun onBillingClientSetupError()
    fun onConsumeFinished(token: String, @BillingClient.BillingResponseCode result: Int)
    fun onPurchasesUpdated(purchases: MutableList<GooResultBean>)
    fun errorByUserCanceled(responseCode: Int)

    /**
     * -1 unknown
     * -2 sku 有误，没有查出对应的信息
     */
    fun error(responseCode: Int)
}

interface GooSkuDetailsListener {
    fun error(responseCode: Int)
    fun onSuccess(skuDetails: MutableList<GooSkuDetails>)
}

data class GooResultBean(
    val mOriginalJson: String,
    val mSignature: String,
    val productId: String,
    val orderId: String,
    val packageName: String,
    val purchaseTime: Long,
    val purchaseToken: String,
    val purchaseState: Int,
    val developerPayload: String?,
    val acknowledged: Boolean,
    val autoRenewing: Boolean
) {
    override fun toString(): String {
        return "Purchase. Json: $mOriginalJson"
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) {
            return true
        }

        if (other !is GooResultBean) {
            return false
        }

        val purchase = other as GooResultBean? ?: return false

        return TextUtils.equals(mOriginalJson, purchase.mOriginalJson) && TextUtils.equals(
            mSignature,
            purchase.mSignature
        )
    }

    override fun hashCode(): Int {
        return mOriginalJson.hashCode()
    }

    fun isPurchaseStatePending(): Boolean {
        return purchaseState == Purchase.PurchaseState.PENDING
    }

    fun isPurchaseStatePurchased(): Boolean {
        return purchaseState == Purchase.PurchaseState.PURCHASED
    }

    companion object {
        @JvmStatic
        fun cloneByPurchase(purchase: Purchase): GooResultBean {
            return GooResultBean(
                purchase.originalJson,
                purchase.signature,
                purchase.sku,
                purchase.orderId,
                purchase.packageName,
                purchase.purchaseTime,
                purchase.purchaseToken,
                purchase.purchaseState,
                purchase.developerPayload,
                purchase.isAcknowledged,
                purchase.isAutoRenewing
            )
        }
    }
}

object GooSecurity {
    private val TAG = "GOO_SECURITY"

    private const val KEY_FACTORY_ALGORITHM = "RSA"
    private const val SIGNATURE_ALGORITHM = "SHA1withRSA"

    /**
     * Verifies that the data was signed with the given signature, and returns the verified
     * purchase.
     * @param base64PublicKey the base64-encoded public key to use for verifying.
     * @param signedData the signed JSON string (signed, not encrypted)
     * @param signature the signature for the data, signed with the private key
     * @throws IOException if encoding algorithm is not supported or key specification
     * is invalid
     */
    @Throws(IOException::class)
    fun verifyPurchase(
        base64PublicKey: String, signedData: String,
        signature: String
    ): Boolean {
        if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey)
            || TextUtils.isEmpty(signature)
        ) {
//            BillingHelper.logWarn(TAG, "Purchase verification failed: missing data.")
            return false
        }

        val key = generatePublicKey(base64PublicKey)
        return verify(key, signedData, signature)
    }

    /**
     * Generates a PublicKey instance from a string containing the Base64-encoded public key.
     *
     * @param encodedPublicKey Base64-encoded public key
     * @throws IOException if encoding algorithm is not supported or key specification
     * is invalid
     */
    @Throws(IOException::class)
    fun generatePublicKey(encodedPublicKey: String): PublicKey {
        try {
            val decodedKey = Base64.decode(encodedPublicKey, Base64.DEFAULT)
            val keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM)
            return keyFactory.generatePublic(X509EncodedKeySpec(decodedKey))
        } catch (e: NoSuchAlgorithmException) {
            // "RSA" is guaranteed to be available.
            throw RuntimeException(e)
        } catch (e: InvalidKeySpecException) {
            val msg = "Invalid key specification: $e"
//            BillingHelper.logWarn(TAG, msg)
            throw IOException(msg)
        }

    }

    /**
     * Verifies that the signature from the server matches the computed signature on the data.
     * Returns true if the data is correctly signed.
     *
     * @param publicKey public key associated with the developer account
     * @param signedData signed data from server
     * @param signature server signature
     * @return true if the data and signature match
     */
    fun verify(publicKey: PublicKey, signedData: String, signature: String): Boolean {
        val signatureBytes: ByteArray
        try {
            signatureBytes = Base64.decode(signature, Base64.DEFAULT)
        } catch (e: IllegalArgumentException) {
//            BillingHelper.logWarn(TAG, "Base64 decoding failed.")
            return false
        }

        try {
            val signatureAlgorithm = Signature.getInstance(SIGNATURE_ALGORITHM)
            signatureAlgorithm.initVerify(publicKey)
            signatureAlgorithm.update(signedData.toByteArray())
            if (!signatureAlgorithm.verify(signatureBytes)) {
//                BillingHelper.logWarn(TAG, "Signature verification failed.")
                return false
            }
            return true
        } catch (e: NoSuchAlgorithmException) {
            // "RSA" is guaranteed to be available.
            throw RuntimeException(e)
        } catch (e: InvalidKeyException) {
//            BillingHelper.logWarn(TAG, "Invalid key specification.")
        } catch (e: SignatureException) {
//            BillingHelper.logWarn(TAG, "Signature exception.")
        }

        return false
    }
}

data class GooSkuDetails(
    val mOriginalJson: String,
    val productId: String,
    val type: String,
    val price: String,
    val price_amount_micros: Long,
    val price_currency_code: String,
    val original_price: String,
    val original_price_micros: Long,
    val title: String,
    val description: String,
    val subscriptionPeriod: String,
    val freeTrialPeriod: String,
    val introductoryPrice: String,
    val introductoryPriceAmountMicros: Long,
    val introductoryPricePeriod: String,
    val introductoryPriceCycles: Int,
    val iconUrl: String
) {
    override fun toString(): String {
        return "SkuDetails: $mOriginalJson"
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) {
            return true
        }
        if (other == null || javaClass != other.javaClass) {
            return false
        }

        val details = other as GooSkuDetails?

        return TextUtils.equals(mOriginalJson, details?.mOriginalJson)
    }

    override fun hashCode(): Int {
        return mOriginalJson.hashCode()
    }

    companion object {
        @JvmStatic
        fun cloneBySkuDetails(skuDetails: SkuDetails): GooSkuDetails {
            return GooSkuDetails(
                skuDetails.originalJson,
                skuDetails.sku,
                skuDetails.type,
                skuDetails.price,
                skuDetails.priceAmountMicros,
                skuDetails.priceCurrencyCode,
                skuDetails.originalPrice,
                skuDetails.originalPriceAmountMicros,
                skuDetails.title,
                skuDetails.description,
                skuDetails.subscriptionPeriod,
                skuDetails.freeTrialPeriod,
                skuDetails.introductoryPrice,
                skuDetails.introductoryPriceAmountMicros,
                skuDetails.introductoryPricePeriod,
                skuDetails.introductoryPriceCycles,
                skuDetails.iconUrl
            )
        }
    }
}